1. Webpack

  1. 安装: npm install webpack -g
    通常我们会将 Webpack 安装到项目的依赖中,这样就可以使用项目本地版本的 Webpack。
    npm install webpack --save-dev
  2. 首先创建一个静态页面 index.html 和一个 JS 入口文件 entry.js;然后编译 entry.js 并打包到 bundle.js:
    webpack entry.js bundle.js

2. Loader

Webpack 本身只能处理 JavaScript 模块,如果要处理其他类型的文件,就需要使用 loader 进行转换。Loader 可以理解为是模块和资源的转换器,
它本身是一个函数,接受源文件作为参数,返回转换的结果。这样,我们就可以通过 require 来加载任何类型的模块或文件,比如 CoffeeScript、 JSX、 LESS 或图片。

  1. Loader 可以在 require() 引用模块的时候添加,也可以在 webpack 全局配置中进行绑定,还可以通过命令行的方式使用。
    我们在页面中引入一个 CSS 文件 style.css,首页将 style.css 也看成是一个模块,然后用 css-loader 来读取它,再用 style-loader 把它插入到页面中。

    1
    2
    /* style.css */
    body { background: yellow; }

    修改 entry.js:

    1
    2
    3
    require("!style!css!./style.css") // 载入 style.css
    document.write('It works.')
    document.write(require('./module.js'))

    安装 loader:

    1
    npm install css-loader style-loader

    重新编译打包

  2. 如果每次 require CSS 文件的时候都要写 loader 前缀,是一件很繁琐的事情。我们可以根据模块类型(扩展名)来自动绑定需要的 loader。
    将 entry.js 中的 require("!style!css!./style.css") 修改为 require("./style.css") ,然后执行:

    1
    2
    3
    webpack entry.js bundle.js --module-bind 'css=style!css'
    // 有些环境下可能需要使用双引号
    webpack entry.js bundle.js --module-bind "css=style!css"

3. 配置文件

Webpack 在执行的时候,除了在命令行传入参数,还可以通过指定的配置文件来执行。默认情况下,会搜索当前目录的 webpack.config.js 文件,
这个文件是一个 node.js 模块,返回一个 json 格式的配置信息对象,或者通过 –config 选项来指定配置文件。
创建一个配置文件 webpack.config.js

1
2
3
4
5
6
7
8
9
10
11
12
var webpack = require('webpack')module.exports = {
entry: './entry.js',
output: {
path: __dirname,
filename: 'bundle.js'
},
module: {
loaders: [
{test: /\.css$/, loader: 'style!css'}
]
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
var webpack = require('webpack');
var commonsPlugin = new webpack.optimize.CommonsChunkPlugin('common.js');
module.exports = {
//插件项
plugins: [commonsPlugin],
//页面入口文件配置
entry: {
index : './src/js/page/index.js'
},
//入口文件输出配置
output: {
path: 'dist/js/page',
filename: '[name].js'
},
module: {
//加载器配置
loaders: [
{ test: /\.css$/, loader: 'style-loader!css-loader' },
{ test: /\.js$/, loader: 'jsx-loader?harmony' },
{ test: /\.scss$/, loader: 'style!css!sass?sourceMap'},
{ test: /\.(png|jpg)$/, loader: 'url-loader?limit=8192'}
]
},
//其它解决方案配置
resolve: {
root: 'E:/github/flux-example/src', //绝对路径
extensions: ['', '.js', '.json', '.scss'],
alias: {
AppStore : 'js/stores/AppStores.js',
ActionType : 'js/actions/ActionType.js',
AppAction : 'js/actions/AppAction.js'
}
}
};

然后运行 webpack,生成 bundle.js

resolve:

1
2
3
4
5
6
7
8
9
10
11
12
resolve: {
//查找module的话从这里开始查找
root: 'E:/github/flux-example/src', //绝对路径
//自动扩展文件后缀名,意味着我们require模块可以省略不写后缀名
extensions: ['', '.js', '.json', '.scss'],
//模块别名定义,方便后续直接引用别名,无须多写长长的地址
alias: {
AppStore : 'js/stores/AppStores.js',//后续直接 require('AppStore') 即可
ActionType : 'js/actions/ActionType.js',
AppAction : 'js/actions/AppAction.js'
}
}


module.loaders 是最关键的一块配置。它告知 webpack 每一种文件都需要使用什么加载器来处理:

1
2
3
4
5
6
7
8
9
10
11
12
13
module: {
//加载器配置
loaders: [
//.css 文件使用 style-loader 和 css-loader 来处理
{ test: /\.css$/, loader: 'style-loader!css-loader' },
//.js 文件使用 jsx-loader 来编译处理
{ test: /\.js$/, loader: 'jsx-loader?harmony' },
//.scss 文件使用 style-loader、css-loader 和 sass-loader 来编译处理
{ test: /\.scss$/, loader: 'style!css!sass?sourceMap'},
//图片文件使用 url-loader 来处理,小于8kb的直接转为base64
{ test: /\.(png|jpg)$/, loader: 'url-loader?limit=8192'}
]
}

“-loader”其实是可以省略不写的,多个loader之间用“!”连接起来。


插件:
插件可以完成更多 loader 不能完成的功能。

插件的使用一般是在 webpack 的配置信息 plugins 选项中指定。

Webpack 本身内置了一些常用的插件,还可以通过 npm 安装第三方插件。

接下来,我们利用一个最简单的 BannerPlugin 内置插件来实践插件的配置和运行,这个插件的作用是给输出的文件头部添加注释信息。

修改 webpack.config.js,添加 plugins

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var webpack = require('webpack')module.exports = {
entry: './entry.js',
output: {
path: __dirname,
filename: 'bundle.js'
},
module: {
loaders: [
{test: /\.css$/, loader: 'style!css'}
]
},
plugins: [
new webpack.BannerPlugin('This file is created by Duyb')
]
}

Webpack遍历所有的依赖,我们希望避免执行第三方代码比如像jQuery,所以我们可以过滤它。Loaders有 include 和 exclude 两个规则。
它可以配置成一个字符串、正则表达式、一个或调或者其他你希望的,我们希望Babel只执行我们自己的文件,所以我们仅 include
我们自己的资源文件夹:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
module.exports = {
entry: './src',
output: {
path: 'builds',
filename: 'bundle.js',
},
module: {
loaders: [{
test: /\.js/,
loader: 'babel',
include: /src/,
}],
}
};

运行:

1
webpack

参数--display-error-details是推荐加上的,方便出错时能查阅更详尽的信息(比如 webpack 寻找模块的过程),从而更好定位到问题:

1
webpack --display-error-details

其他主要的参数有:

1
2
3
4
5
6
7
$ webpack --config XXX.js //使用另一份配置文件(比如webpack.config2.js)来打包
$ webpack --watch //监听变动并自动打包
$ webpack -p //压缩混淆脚本,这个非常非常重要!
$ webpack -d //生成map映射文件,告知哪些模块被最终打包到哪里了

4. 开发环境

  1. 当项目逐渐变大,webpack 的编译时间会变长,可以通过参数让编译的输出内容带有进度和颜色。
    webpack --progress --colors
    如果不想每次修改模块后都重新编译,那么可以启动监听模式。开启监听模式后,没有变化的模块会在编译后缓存到内存中,而不会每次都被重新编译,所以监听模式的整体速度是很快的。
    webpack --progress --colors --watch
    当然,使用 webpack-dev-server 开发服务是一个更好的选择。它将在 localhost:8080 启动一个 express 静态资源 web 服务器,
    并且会以监听模式自动运行 webpack,在浏览器打开 http://localhost:8080/http://localhost:8080/webpack-dev-server/
    可以浏览项目中的页面和编译后的资源输出,并且通过一个 socket.io 服务实时监听它们的变化并自动刷新页面。

    1
    2
    3
    4
    5
    // 安装
    $ npm install webpack-dev-server -g
    // 运行
    $ webpack-dev-server --progress --colors
  2. 热更新“Live Reload”以及热替换“Hot Module Replacement”(即HMR)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//方式 1:
// 全局安装
npm install webpack-dev-server --save
// 终端输入
$ webpack-dev-server --inline --hot
//用法 2:
// 添加到package.json scripts"scripts": {
"start": "webpack-dev-server --inline --hot",
...
}
// 运行:
$ npm start
// 浏览器预览:
http://localhost:8080

  1. 环境NODE_ENV:使用cross-env解决跨平台设置NODE_ENV的问题
    • 安装cross-env:npm install cross-env --save-dev
    • NODE_ENV=xxxxxxx前面添加cross-env就可以了。
      1
      2
      3
      4
      5
      6
      "scripts": {
      "test": "webpack --colors",
      "watch": "cross-env NODE_ENV=development webpack --progress --colors --watch",
      "build": "cross-env NODE_ENV=production webpack --colors",
      "server": "webpack-dev-server --open --inline"
      }

5. 其他配置

  • 生成单独的css文件npm install extract-text-webpack-plugin --save-dev
    先安装一下:npm install style-loader css-loader –save-dev,然后再重启下webpack-dev-server,就可以加载样式了,我们发现webpack是把样式动态插到了head标签的style里面,但是一般并不希望直接写到head里面,而是独立的一个css文件,这个时候借助一个分离css的插件就可以了:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    var ExtractTextPlugin = require("extract-text-webpack-plugin");
    module.exports = {
    module: {
    loaders: [
    // Extract css files
    {
    test: /\.css$/,
    loader: ExtractTextPlugin.extract("style-loader", "css-loader")
    },
    ]
    },
    plugins: [
    new ExtractTextPlugin("[name].css")
    ]
    };
  • 内置插件OccurenceOrderPlugin :为组件分配ID,通过这个插件webpack可以分析和优先考虑使用最多的模块,并为它们分配最小的ID

  • Common chunk:commonChunkPlugin
  • 压缩和版本号:内置插件UglifyJsPlugin
  • 自动生成 HTML 文件 html-webpack-pluginnpm install html-webpack-plugin --save-dev
    这个插件用来简化创建服务于 webpack bundle 的 HTML 文件,尤其是对于在文件名中包含了 hash 值,而这个值在每次编译的时候都发生变化的情况。
    你既可以让这个插件来帮助你自动生成 HTML 文件,也可以使用 lodash 模板加载生成的 bundles,或者自己加载这些 bundles。
  • 在一个生成环境构建之前我们也许希望我们的builds文件夹是清空的 clean-webpack-plugin:npm install clean-webpack-plugin --save-dev
  • open-browser-webpack-plugin
  • Environment flags:Environment flags (source)
    You can enable some codes only in development environment with environment flags.

    main.js

    1
    2
    3
    4
    5
    document.write('<h1>Hello World</h1>');
    if (__DEV__) {
    document.write(new Date());
    }

index.html

1
2
3
4
5
<html>
<body>
<script src="bundle.js"></script>
</body>
</html>

webpack.config.js

1
2
3
4
5
6
7
8
9
10
11
12
13
var webpack = require('webpack');
var devFlagPlugin = new webpack.DefinePlugin({
__DEV__: JSON.stringify(JSON.parse(process.env.DEBUG || 'false'))
});
module.exports = {
entry: './main.js',
output: {
filename: 'bundle.js'
},
plugins: [devFlagPlugin]
};

Now pass environment variable into webpack.

1
2
3
4
5
6
# Linux & Mac
$ env DEBUG=true webpack-dev-server
# Windows
$ set DEBUG=true
$ webpack-dev-server
  • webpack如何配置 插入jquery+jquery插件?

如果想引用第三方插件,不通过npm的方式,怎样加载?比如我想用一个jquery的插件,但是npm找不到,我下载到本地之后怎么加载,怎么配置??
目的就是想用webpack + jquery + jquery插件 管理项目,请问webpack怎么配置?
https://github.com/ruanyf/webpack-demos/tree/master/demo12
原来,对于不支持CommonJS规范的模块,需要安装:imports-loader
局部安装:
npm install imports-loader --save-dev

然后呢:
entry.js文件设置:
var tab = require('imports?$=jquery!./jquery.tab');

webpack.confing.js配置:

1
2
3
4
5
6
7
8
plugins: [
new webpack.ProvidePlugin({
$: "jquery",
jQuery: "jquery",
"window.jQuery": "jquery"
}),
new webpack.optimize.CommonsChunkPlugin('vendor', 'vendor.js')//这是第三方库打包生成的文件
]

6. 我在自己项目里实现的HMR:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
// server.js
/**
* Created by Duyb on 2016/11/25.
*/
/**
* Module dependencies.
*/
var express = require('express');
var http = require('http');
var path = require('path');
var favicon = require('serve-favicon');
var morgan = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var ejs = require('ejs');
var debug = require('debug')('vue-webpack:server');
var webpack = require('webpack');
var config = require('../webpack.config.js');
var WebpackDevMiddleware = require('webpack-dev-middleware');
var WebpackHotMiddleware = require('webpack-hot-middleware');
var compiler = webpack(config); // 调用webpack并把配置传递过去
var routes = require('../src/routes/index');
var app = express(); // 实例化Express对象
var mode = app.get('env'); // development production
// 注册中间件
if (mode === 'development') {
console.log('config.output.publicPath : ', config.output.publicPath);
// 使用 webpack-dev-middleware 中间件
var devMiddleware = WebpackDevMiddleware(compiler, {
publicPath: config.output.publicPath,
stats: {
color: true,
chunks: false
}
})
// 使用 webpack-hot-middleware 中间件
var hotMiddleware = WebpackHotMiddleware(compiler, {
log: console.log
});
// webpack插件,监听html文件改变事件
compiler.plugin('compilation', function (compilation) {
compilation.plugin('html-webpack-plugin-after-emit', function (data, cb) {
// 发布事件
hotMiddleware.publish({ action: 'reload' })
cb()
})
})
app.use(devMiddleware);
app.use(hotMiddleware);
}
//******************************* ↓↓↓↓ Express ↓↓↓↓ *******************************
// view engine setup
app.set('views', path.join(__dirname, '../dist/views')); // 视图
// app.set('view engine', 'jade'); // 模板引擎
// html
app.engine('.html', ejs.__express);
app.set('view engine', 'html');
// 禁用X-Powered-By
app.disable('x-powered-by');
// uncomment after placing your favicon in /public
app.use(favicon(path.join(__dirname, '../src/assets/img/logo.png')));
app.use(morgan('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
if (mode === 'production') {
app.use(express.static(path.join(__dirname, '../dist'))); // 利用 Express 托管静态文件 http://www.expressjs.com.cn/starter/static-files.html
}
// routes
app.use('/', routes);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
var err = new Error('Not Found');
err.status = 404;
next(err);
});
/**
* Get port from environment and store in Express.
*/
var port = normalizePort(process.env.PORT || '3008');
app.set('port', port);
/**
* Create HTTP server.
*/
var server = http.createServer(app);
/**
* Listen on provided port, on all network interfaces.
*/
server.listen(port, () => {
console.log( 'vue-webpack started in ' + app.get('env') +' mode, on http://localhost:' +
app.get('port') + ' .' );
});
server.on('error', onError);
server.on('listening', onListening);
/**
* Normalize a port into a number, string, or false.
*/
function normalizePort(val) {
var port = parseInt(val, 10);
if (isNaN(port)) {
// named pipe
return val;
}
if (port >= 0) {
// port number
return port;
}
return false;
}
/**
* Event listener for HTTP server "error" event.
*/
function onError(error) {
if (error.syscall !== 'listen') {
throw error;
}
var bind = typeof port === 'string'
? 'Pipe ' + port
: 'Port ' + port;
// handle specific listen errors with friendly messages
switch (error.code) {
case 'EACCES':
console.error(bind + ' requires elevated privileges');
process.exit(1);
break;
case 'EADDRINUSE':
console.error(bind + ' is already in use');
process.exit(1);
break;
default:
throw error;
}
}
/**
* Event listener for HTTP server "listening" event.
*/
function onListening() {
var addr = server.address();
var bind = typeof addr === 'string'
? 'pipe ' + addr
: 'port ' + addr.port;
debug('Listening on ' + bind);
}
module.exports = app;